package com.jeebey.mybatis.query;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import com.google.common.collect.Lists;
import com.jeebey.mybatis.annotation.Column;
import com.jeebey.mybatis.annotation.GeneratedValue;
import com.jeebey.mybatis.annotation.Id;
import com.jeebey.mybatis.annotation.Table;
import com.jeebey.mybatis.annotation.Transient;
import com.jeebey.mybatis.utils.StrCharUtil;

/**
 * 条件查询
 * 
 * @since 1.0
 * @author <a href="mailto:84961426@qq.com">JuST4iT</a>
 * @version $Id: Condition.java Feb 18, 2016 6:06:32 PM $
 */
public class Condition {
	public static final Class<? extends Annotation> JPA_ID = Id.class;
	public static final Class<? extends Annotation> JPA_TABLE = Table.class;
	public static final Class<? extends Annotation> JPA_COLUMN = Column.class;
	public static final Class<? extends Annotation> JPA_TRANSIENT = Transient.class;
	public static final Class<? extends Annotation> JPA_GENERATED_VALUE = GeneratedValue.class;

	private StringBuffer buffer = new StringBuffer();

	private List<Object> params = Lists.newArrayList();

	public StringBuffer getBuffer() {
		return buffer;
	}

	public void setBuffer(StringBuffer buffer) {
		this.buffer = buffer;
	}

	public List<Object> getParams() {
		return params;
	}

	public void setParams(List<Object> params) {
		this.params = params;
	}

	// SQL 非空
	public boolean isNonempty() {
		return this.getBuffer().length() != 0;
	}

	public boolean isMultiple() {
		int iCnt1 = StrCharUtil.countSubStr(this.getBuffer().toString(), "?");
		int iCnt2 = StrCharUtil.countSubStr(this.getBuffer().toString(), "IS NULL");
		int iCnt3 = StrCharUtil.countSubStr(this.getBuffer().toString(), "IS NOT NULL");
		return (iCnt1 + iCnt2 + iCnt3) > 1;
	}

	public Condition() {

	}

	/** 暂时不优化使用 */
	public Condition(String field) {

	}

	public Condition(Condition... conditions) {
		this(true, conditions);
	}

	public Condition(boolean bAnd, Condition... conditions) {
		if (conditions != null) {
			if (bAnd) {
				for (Condition condition : conditions) {
					this.and(condition);
				}
			} else {
				for (Condition condition : conditions) {
					this.or(condition);
				}
			}
		}
	}

	@Deprecated
	@SuppressWarnings("unused")
	private Condition and(Condition... conditions) {
		// 实现1
		if (conditions != null) {
			for (Condition condition : conditions) {
				this.and(condition);
			}
		}
		// 实现2
		// List<Condition> list=Lists.newArrayList(conditions);
		// if(this.isNonempty()){list.add(0, this);}
		// Condition _condition=Condition.AND(list.toArray(new Condition[0]));
		// this.buffer=_condition.buffer;
		// this.params=_condition.params;
		return this;
	}

	@Deprecated
	@SuppressWarnings("unused")
	private Condition or(Condition... conditions) {
		// 实现1
		if (conditions != null) {
			for (Condition condition : conditions) {
				this.or(condition);
			}
		}
		// 实现2 歧义
		// List<Condition> list=Lists.newArrayList(conditions);
		// if(this.isNonempty()){list.add(0, this);}
		// Condition _condition=Condition.OR(list.toArray(new Condition[0]));
		// this.buffer=_condition.buffer;
		// this.params=_condition.params;
		return this;
	}

	public Criterion toCriterion() {
		return new Criterion(this);
	}

	public Condition and(Condition condition) {
		if (!condition.isNonempty()) {
			return this;
		}
			
		boolean bPrefix = this.isNonempty();
		boolean bPrevkh = this.isMultiple();
		boolean bNextkh = condition.isMultiple();
		this.getBuffer().insert(0, bPrevkh ? "(" : "");
		this.getBuffer().append(bPrevkh ? ")" : "").append(bPrefix ? " AND " : "").append(bNextkh ? "(" : "")
				.append(condition.getBuffer()).append(bNextkh ? ")" : "");
		this.params.addAll(condition.getParams());
		return this;
	}

	public Condition or(Condition condition) {
		if (!condition.isNonempty()) {
			return this;
		}
			
		boolean bPrefix = this.isNonempty();
		boolean bPrevkh = this.isMultiple();
		boolean bNextkh = condition.isMultiple();
		this.getBuffer().insert(0, bPrevkh ? "(" : "");
		this.getBuffer().append(bPrevkh ? ")" : "").append(bPrefix ? " OR " : "").append(bNextkh ? "(" : "")
				.append(condition.getBuffer()).append(bNextkh ? ")" : "");
		this.params.addAll(condition.getParams());
		return this;
	}

	public static Condition EMPEY() {
		return new Condition();
	}

	public static Condition AND(Condition... conditions) {
		Condition _condition = new Condition();
		for (Condition condition : conditions) {
			if (!condition.isNonempty()) {
				continue;
			}
				
			boolean bNextkh = condition.isMultiple();
			_condition.getBuffer().append(" AND ").append(bNextkh ? "(" : "").append(condition.getBuffer())
					.append(bNextkh ? ")" : "");
			_condition.params.addAll(condition.getParams());
		}
		_condition.getBuffer().delete(0, 5);
		return _condition;
	}

	public static Condition OR(Condition... conditions) {
		Condition _condition = new Condition();
		for (Condition condition : conditions) {
			if (!condition.isNonempty()) {
				continue;
			}
				
			boolean bNextkh = condition.isMultiple();
			_condition.getBuffer().append(" OR ").append(bNextkh ? "(" : "").append(condition.getBuffer())
					.append(bNextkh ? ")" : "");
			_condition.params.addAll(condition.getParams());
		}
		_condition.getBuffer().delete(0, 4);
		return _condition;
	}

	public static Condition EQ(String field, Object value) {
		Condition condition = new Condition();
		if (value != null) {
			condition.getBuffer().append(field).append(" = ?");
			condition.params.add(value);
		}
		return condition;
	}

	public static Condition NEQ(String field, Object value) {
		Condition condition = new Condition();
		if (value != null) {
			condition.getBuffer().append(field).append(" != ?");
			condition.params.add(value);
		}
		return condition;
	}

	public static Condition GT(String field, Object value) {
		Condition condition = new Condition();
		if (value != null) {
			condition.getBuffer().append(field).append(" > ?");
			condition.params.add(value);
		}
		return condition;
	}

	public static Condition NGT(String field, Object value) {
		Condition condition = new Condition();
		if (value != null) {
			condition.getBuffer().append(field).append(" <= ?");
			condition.params.add(value);
		}
		return condition;
	}

	public static Condition LT(String field, Object value) {
		Condition condition = new Condition();
		if (value != null) {
			condition.getBuffer().append(field).append(" < ?");
			condition.params.add(value);
		}
		return condition;
	}

	public static Condition NLT(String field, Object value) {
		Condition condition = new Condition();
		if (value != null) {
			condition.getBuffer().append(field).append(" >= ?");
			condition.params.add(value);
		}
		return condition;
	}

	public static Condition NOTLIKE(String field, Object value) {
		Condition condition = new Condition();
		if (value != null) {
			condition.getBuffer().append(field).append(" NOT LIKE ?");
			condition.params.add(value);
		}
		return condition;
	}

	public static Condition LIKE(String field, Object value) {
		Condition condition = new Condition();
		if (value != null) {
			condition.getBuffer().append(field).append(" LIKE ?");
			condition.params.add(value);
		}
		return condition;
	}

	public static Condition NOTLIKE(String field, Object value, String prev, String next) {
		Condition condition = new Condition();
		if (value != null) {
			condition.getBuffer().append(field).append(" NOT LIKE ?");
			condition.params.add(StringUtils.defaultString(prev) + value + StringUtils.defaultString(next));
		}
		return condition;
	}

	public static Condition LIKE(String field, Object value, String prev, String next) {
		Condition condition = new Condition();
		if (value != null) {
			condition.getBuffer().append(field).append(" LIKE ?");
			condition.params.add(StringUtils.defaultString(prev) + value + StringUtils.defaultString(next));
		}
		return condition;
	}

	// 待测试
	public static Condition NOTEXISTS(String sql, Object... values) {
		Condition condition = new Condition();
		condition.getBuffer().append(" NOT EXISTS (").append(sql).append(")");
		if (values != null) {
			for (Object value : values) {
				condition.params.add(value);
			}
		}
		return condition;
	}

	// 待测试
	public static Condition EXISTS(String sql, Object... values) {
		Condition condition = new Condition();
		condition.getBuffer().append(" EXISTS (").append(sql).append(")");
		if (values != null) {
			for (Object value : values) {
				condition.params.add(value);
			}
		}
		return condition;
	}

	// 待测试
	public static Condition NOTIN(String field, Object... values) {
		Condition condition = new Condition();
		if (values != null && values.length > 0) {
			StringBuffer sql_seats = new StringBuffer("");
			for (Object value : values) {
				sql_seats.append(",?");
				condition.params.add(value);
			}
			condition.getBuffer().append(field).append(" NOT IN (").append(sql_seats.substring(1)).append(")");
		}
		return condition;
	}

	public static Condition IN(String field, Object... values) {
		Condition condition = new Condition();
		if (values != null && values.length > 0) {
			StringBuffer sql_seats = new StringBuffer("");
			for (Object value : values) {
				sql_seats.append(",?");
				condition.params.add(value);
			}
			condition.getBuffer().append(field).append(" IN (").append(sql_seats.substring(1)).append(")");
		}
		return condition;
	}

	public static Condition BW(String field, Object value1, Object value2) {
		Condition condition = new Condition();
		condition.getBuffer().append(field).append(" BETWEEN ? AND ?");
		condition.params.add(value1);
		condition.params.add(value2);
		return condition;
	}

	/** 只支持 mysql is not null */
	public static Condition NOTNUL(String field) {
		Condition condition = new Condition();
		condition.getBuffer().append(field).append(" IS NOT NULL");
		return condition;
	}

	/** 只支持 mysql is null */
	public static Condition NUL(String field) {
		Condition condition = new Condition();
		condition.getBuffer().append(field).append(" IS NULL");
		return condition;
	}

	/** 只支持 mysql is null */
	public static Condition NUL(String field, Object value) {
		Condition condition = new Condition();
		if (value == null) {
			condition.getBuffer().append(field).append(" IS NULL");
		} else {
			condition.getBuffer().append(field).append(" = ?");
			condition.params.add(value);
		}
		return condition;
	}

	public static Condition SQL(String sql, Object... values) {
		Condition condition = new Condition();
		condition.getBuffer().append(sql);
		if (values != null) {
			for (Object value : values) {
				condition.params.add(value);
			}
		}
		return condition;
	}

	public static Condition SQL(String sql) {
		Condition condition = new Condition();
		condition.getBuffer().append(sql);
		return condition;
	}

	/**
	 * 非空字段 对象查询
	 * 
	 * @param values
	 *            bAnd:true AND | bAnd:false OR
	 * @return
	 */
	public static Condition EXAMPLE(boolean bAnd, Object... values) {
		List<Condition> _conditions = Lists.newArrayList();
		for (Object obj : values) {
			Field[] fields = obj.getClass().getDeclaredFields();
			Condition _condition = new Condition();
			List<String> examples = Lists.newArrayList();
			try {
				for (Field field : fields) {
					field.setAccessible(true);
					Object value = field.get(obj);
					if (value != null && !field.isAnnotationPresent(JPA_TRANSIENT)) {
						if (field.isAnnotationPresent(JPA_COLUMN)) {
							Column column = (Column) field.getAnnotation(JPA_COLUMN);
							String _escape = column.escape() ? "`" : "";
							String _column = StringUtils.isEmpty(column.name()) ? field.getName() : column.name();
							examples.add(_escape + _column + _escape + " = ?");
						} else {
							examples.add(field.getName() + " = ?");
						}
						_condition.params.add(value);
					}
				}
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
			_condition.getBuffer().append(StringUtils.join(examples, " AND "));
			_conditions.add(_condition);
		}
		return bAnd ? Condition.AND(_conditions.toArray(new Condition[0])) : Condition.OR(_conditions
				.toArray(new Condition[0]));
	}

	/**
	 * 非空字段 对象查询
	 * 
	 * @param values
	 *            defautl: OR
	 * @return
	 */
	public static Condition EXAMPLE(Object... values) {
		return EXAMPLE(false, values);
	}
}
